package com.file.manage.domain.service.impl;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.json.JSONUtil;
import com.file.manage.domain.data.FileTreeDTO;
import com.file.manage.domain.data.FileVO;
import com.file.manage.domain.entity.File;
import com.file.manage.domain.entity.User;
import com.file.manage.domain.entity.UserFileMapping;
import com.file.manage.domain.entity.convertor.FileConvertor;
import com.file.manage.domain.enums.FileType;
import com.file.manage.domain.request.FileCopyMoveRequest;
import com.file.manage.domain.response.UploadTmpFileResponse;
import com.file.manage.domain.service.FileService;
import com.file.manage.domain.service.OperationLogsService;
import com.file.manage.domain.verifiers.FileVerifier;
import com.file.manage.infrastructure.base.CommonResult;
import com.file.manage.infrastructure.base.service.impl.BaseServiceImpl;
import com.file.manage.infrastructure.config.CurrentUserCache;
import com.file.manage.infrastructure.dao.UserDao;
import com.file.manage.infrastructure.dao.UserFileMappingDao;
import com.file.manage.infrastructure.dao.impl.FileDaoImpl;
import com.file.manage.infrastructure.dao.FolderStencilDao;
import com.file.manage.infrastructure.utils.ConvertUtil;
import com.file.manage.infrastructure.utils.FileUtils;
import com.file.manage.infrastructure.utils.IdGenerator;
import com.file.manage.infrastructure.utils.TreeUtil;
import com.github.benmanes.caffeine.cache.Cache;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.jspecify.annotations.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author H41
 * @Type IFileServiceImpl.java
 * @date 2025/5/31 12:28
 * @Desc
 */
@Service
@Slf4j
public class FileServiceImpl extends BaseServiceImpl<FileDaoImpl, File> implements FileService {
    @Resource
    private Cache<String, Object> caffeineCache;
    @Resource
    private UserDao userDao;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private FolderStencilDao folderStencilDao;

    @Resource
    private OperationLogsService operationLogsService;
    @Resource
    private UserFileMappingDao userFileMappingDao;

    @Override
    public List<FileTreeDTO> selectTree(String pid) {
        List<File> list = baseDao.list();
        if (CollUtil.isEmpty(list)) {
            return new ArrayList<>();
        }
        list = currentUserFileFilter(list);
        list.sort(Comparator.comparing(File::getCreatedTime).reversed());
        return TreeUtil.getDirectoryTreeData(ConvertUtil.convertList(list, FileTreeDTO.class), pid);
    }

    @Override
    public void createFolder(String name, String parentId) {
        Assert.isFalse(parentId == null && !CurrentUserCache.isAdmin(), "管理员才能创建一级文件夹");
        File file = this.baseDao.selectByParentIdAndName(parentId, name);
        Assert.isNull(file, name + "文件夹已存在");


        List<File> savedFolders = new ArrayList<>();
        File newFolder = new File().setParentId(parentId)
                .setName(name).setType(FileType.FOLDER);
        newFolder.setId(IdGenerator.identity());
        savedFolders.add(newFolder);

        int prentLevel = 1;
        if (StrUtil.isNotBlank(parentId)) {
            File parentFile = this.baseDao.getById(parentId);
            Assert.notNull(parentFile, name + "父级文件夹不存在");

            List<String> parentNames = allUpperLevelFolder(parentId);
            if (CollUtil.isNotEmpty(parentNames)) prentLevel = parentNames.size() + 1;
        }
        // 获取模版文件夹
        ListMultimap<Integer, String> prentLevel2FolderStencilMap = folderStencilDao.prentLevel2FolderStencilMap();
        List<String> folderStencil = prentLevel2FolderStencilMap.get(prentLevel);

        if (CollUtil.isNotEmpty(folderStencil)) {
            new HashSet<>(folderStencil).forEach(n -> {
                savedFolders.add(new File().setType(FileType.FOLDER).setName(n).setParentId(newFolder.getId()));
            });
        }
        if (CollUtil.isNotEmpty(savedFolders)) {
            this.baseDao.saveBatch(savedFolders);
        }
        for (File savedFolder : savedFolders) {
            operationLogsService.recordingCreateFolder(getPath(savedFolder));
        }
    }

    @Override
    public void updateNameById(String id, String name) {
        File file = baseDao.getById(id);
        Assert.notNull(file, name + "文件不存在");
        FileVerifier.lockVerify(file);

        List<String> list = allUpperLevelFolder(file.getParentId());
        String odlFilePath = StrUtil.join("/", list) + "/" + file.getName();
        file.setName(name);
        file.updateById();

        String newFilePath = StrUtil.join("/", list) + "/" + name;
        operationLogsService.recordingUpdate(odlFilePath, newFilePath);

    }

    @Override
    public void uploadFileByFolders(List<String> folders, String name, MultipartFile multipartFile) throws IOException {
        List<File> savedFiles = new ArrayList<>();
        String parentId = getParentIdByFolderName(folders, savedFiles);
        savedFiles.add(saveFile(name, multipartFile, parentId));

        this.baseDao.saveBatch(savedFiles);

        for (File savedFile : savedFiles) {
            switch (savedFile.getType()) {
                case FOLDER:
                    operationLogsService.recordingCreateFolder(getPath(savedFile));
                    break;
                case FILE:
                    operationLogsService.recordingUpload(getPath(savedFile));
                    break;
            }
        }
    }

    private String getParentIdByFolderName(List<String> folders, List<File> savedFiles) {
        // 获取模版文件夹
        ListMultimap<Integer, String> prentLevel2FolderStencilMap = folderStencilDao.prentLevel2FolderStencilMap();
        String parentId = null;
        int prentLevel = 1;
        for (String folder : folders) {
            File file = this.baseDao.selectByParentIdAndName(parentId, folder);
            if (file == null) {
                file = new File().setParentId(parentId).setName(folder).setType(FileType.FOLDER);
                file.setId(IdGenerator.identity());
                savedFiles.add(file);

                List<String> folderStencilNames = prentLevel2FolderStencilMap.get(prentLevel);
                if (CollUtil.isNotEmpty(folderStencilNames)) {
                    Set<String> folderStencilNameSet = new HashSet<>(folderStencilNames);
                    for (String name : folderStencilNameSet) {
                        savedFiles.add(new File().setType(FileType.FOLDER).setName(name).setParentId(file.getId()));
                    }

                }
            } else {
//                FileVerifier.lockVerify(file);
            }
            parentId = file.getId();
        }
        return parentId;
    }

    private File saveFile(String name, MultipartFile multipartFile, String parentId) throws IOException {
        // 看看文件是否存在
//        List<File> files = this.baseDao.selectByParentId(parentId);
//        Set<String> nameSet = files.stream().map(File::getName).collect(Collectors.toSet());
//        // 处理重复文件名,重复 拼接-副本
//        name = doHandleRepeatLabel(nameSet, name);
        File file = this.baseDao.selectByParentIdAndName(parentId, name);
        Assert.isNull(file, name + "文件重名");

        java.io.File downLoadFile = FileUtils.downLoad(FileUtils.toFileName(name), multipartFile.getInputStream());
        Assert.notNull(downLoadFile, name + "文件上传失败");
        return new File().setFileUrl(downLoadFile.getPath()).setParentId(parentId).setName(name).setType(FileType.FILE);
    }

    @Override
    public void uploadFileByParentId(String parentId, String name, MultipartFile multipartFile) throws IOException {
        File parentFile = this.baseDao.getById(parentId);
        Assert.notNull(parentFile, "父级文件夹不存在");
//        FileVerifier.lockVerify(parentFile);
        File file = saveFile(name, multipartFile, parentId);
        file.insert();

        operationLogsService.recordingUpload(getPath(file));
    }

    // 获取所有所有上级文件夹
    private List<String> allUpperLevelFolder(String parentId) {
        if (StrUtil.isBlank(parentId)) return new ArrayList<>();
        File parentFile = this.baseDao.getById(parentId);
        if (parentFile != null && parentFile.getType() == FileType.FOLDER) {
            if (parentFile.getParentId() != null) {
                List<String> uppers = allUpperLevelFolder(parentFile.getParentId());
                uppers.add(parentFile.getName());
                return uppers;
            } else {
                return CollUtil.newArrayList(parentFile.getName());
            }
        }
        return new ArrayList<>();
    }

    @Override
    public UploadTmpFileResponse uploadTmpFile(List<String> folders, String name, MultipartFile multipartFile) throws IOException {
        List<File> savedFiles = new ArrayList<>();
        String parentId = getParentIdByFolderName(folders, savedFiles);
        if (parentId != null) {
            File file = this.baseDao.selectByParentIdAndName(parentId, name);
            Assert.isNull(file, name + "文件重名");
        }
        java.io.File loadFile = FileUtils.downLoadTmpFile(FileUtils.toFileName(name), multipartFile.getInputStream());
        Assert.notNull(loadFile, name + "文件上传失败");

        tmpFileCheck(folders, name);

        return new UploadTmpFileResponse(loadFile.getPath(), name, folders, parentId);
    }

    @Override
    public void uploadZip(String parentId, MultipartFile multipartFile) throws IOException {
        Assert.isTrue(multipartFile.getOriginalFilename() != null && multipartFile.getOriginalFilename().endsWith(".zip"), "上传文件必须是zip格式");
        if (StrUtil.isNotBlank(parentId)) {
            File parentFile = this.baseDao.getById(parentId);
            Assert.notNull(parentFile, "父级文件夹不存在");
        }
        String targetPath = IdGenerator.identity();
        FileUtils.unzip(multipartFile.getInputStream(), targetPath);
        // 获取所有子项（包含嵌套目录）
        java.io.File targetFile = new java.io.File(targetPath);
        List<java.io.File> allFiles = List.of(FileUtil.ls(targetFile.getAbsolutePath()));
        ArrayList<File> savaFiles = new ArrayList<>();
        saveZipFile(parentId, allFiles, savaFiles);
        if (CollUtil.isNotEmpty(savaFiles)) {
            this.baseDao.saveBatch(savaFiles);
        }
        FileUtil.del(targetFile);
    }

    void saveZipFile(String parentId, List<java.io.File> allFiles, List<File> files) {
        for (java.io.File file : allFiles) {
            List<File> fileList = this.baseDao.selectByParentId(parentId);
            Map<String, File> name2FileMap = fileList.stream().collect(Collectors.toMap(File::getName, i -> i));
            File efile = name2FileMap.get(file.getName());
            if (file.isDirectory()) {
                if (efile == null) {
                    efile = new File();
                    efile.setType(FileType.FOLDER);
                    efile.setParentId(parentId);
                    efile.setName(file.getName());
                    efile.setId(IdGenerator.identity());
                    files.add(efile);
                }
                saveZipFile(efile.getId(), List.of(FileUtil.ls(file.getAbsolutePath())), files);
            } else {
                String newLabel = doHandleRepeatLabel(name2FileMap.keySet(), file.getName());
                efile = new File();
                efile.setType(FileType.FILE);
                efile.setParentId(parentId);
                efile.setName(newLabel);
                try (InputStream inputStream = new FileInputStream(file)) {
                    java.io.File newFile = FileUtils.downLoad(FileUtils.toFileName(newLabel), inputStream);
                    efile.setFileUrl(newFile.getPath());
                } catch (IOException e) {
                    log.error("文件上传失败", e);
                }
                efile.setId(IdGenerator.identity());
                files.add(efile);
            }
        }
    }

    private void tmpFileCheck(List<String> folders, String name) {

        // 缓存的临时文件名，是否有重名
        String str = String.join("/", folders) + "/" + name;
        caffeineCache.put(str, "");
        Object o = caffeineCache.getIfPresent(str);
        Assert.isNull(o, name + "文件重名");
    }

    @Override
    public void tmp2FormalFile(List<UploadTmpFileResponse> responses) throws IOException {
        List<File> savedFiles = new ArrayList<>();
        for (UploadTmpFileResponse r : responses) {

            String parentId = getParentIdByFolderName(r.getFolders(), savedFiles);
            File file = this.baseDao.selectByParentIdAndName(parentId, r.getFileName());
            Assert.isNull(file, r.getFileName() + "文件重名");

            java.io.File downLoadFile = FileUtils.tmp2FormalFile(r.getTmpFileUrl(), FileUtils.toFileName(r.getFileName()));
            Assert.notNull(downLoadFile, r.getFileName() + "文件上传失败");

            savedFiles.add(new File().setFileUrl(downLoadFile.getPath()).setParentId(parentId).setName(r.getFileName()).setType(FileType.FILE));
            // 移除临时文件缓存
            String str = String.join("/", r.getFolders()) + "/" + r.getFileName();
            caffeineCache.invalidate(str);
        }
        this.baseDao.saveBatch(savedFiles);
        for (File savedFile : savedFiles) {
            switch (savedFile.getType()) {
                case FOLDER:
                    operationLogsService.recordingCreateFolder(getPath(savedFile));
                    break;
                case FILE:
                    operationLogsService.recordingUpload(getPath(savedFile));
                    break;
            }
        }
    }

    @Override
    public List<FileVO> getByParentId(String parentId) {

        List<File> list = baseDao.list();
        if (CollUtil.isEmpty(list)) return new ArrayList<>();

        ListMultimap<String, File> parent2FileMap = ArrayListMultimap.create();
        list.forEach(i -> parent2FileMap.put(i.getParentId(), i));

        List<File> files = parent2FileMap.get(parentId);
        files = currentUserFileFilter(files);

        if (CollUtil.isNotEmpty(files)) {
            List<String> userIds = new ArrayList<>();
            for (File file : files) {
                if (StrUtil.isNotBlank(file.getCreatedBy())) userIds.add(file.getCreatedBy());
                if (StrUtil.isNotBlank(file.getUpdatedBy())) userIds.add(file.getUpdatedBy());
            }
            Map<String, User> userMap = userDao.mapByIds(userIds);
            return files.stream().map(i -> FileConvertor.INSTANCE.toFileVO(i,
                    userMap.get(i.getCreatedBy()).getName(),
                    userMap.get(i.getUpdatedBy()) != null ? userMap.get(i.getUpdatedBy()).getName() : null,
                    i.getType() == FileType.FOLDER ? fileStatusStatistics(i.getId(),parent2FileMap) : new FileStatusStatistics()
            )).toList();
        }
        return List.of();
    }

    private FileStatusStatistics fileStatusStatistics(String parentId, ListMultimap<String, File> parent2FileMap) {
        return fileStatusStatistics(parentId, new FileStatusStatistics(), parent2FileMap);
    }

    private FileStatusStatistics fileStatusStatistics(String parentId, FileStatusStatistics statusStatistics, ListMultimap<String, File> parent2FileMap) {
        List<File> files = parent2FileMap.get(parentId);
        if (CollUtil.isNotEmpty(files)) {
            for (File file : files) {
                switch (file.getType()) {
                    case FILE -> {
                        if (file.getDelTag()) statusStatistics.addDelTagFileQty();
                        if (!file.getIsLock()) statusStatistics.addNoLockFileQty();
                    }
                    case FOLDER -> {
                        fileStatusStatistics(file.getId(), statusStatistics, parent2FileMap);
                    }
                }
            }
        } else {
            statusStatistics.addLeafFolderNoFileQty();
        }
        return statusStatistics;
    }

    @Data
    public static class FileStatusStatistics {
        @Schema(title = "没有文件的叶子文件夹数量")
        private int leafFolderNoFileQty;

        @Schema(title = "没有加锁文件数量")
        private int noLockFileQty;

        @Schema(title = "删除标记文件数量")
        private int delTagFileQty;

        void addLeafFolderNoFileQty() {
            this.leafFolderNoFileQty += 1;
        }

        void addNoLockFileQty() {
            this.noLockFileQty += 1;
        }

        void addDelTagFileQty() {
            this.delTagFileQty += 1;
        }
    }

    private List<File> currentUserFileFilter(List<File> files) {
        if (!CurrentUserCache.isAdmin()) {
            List<UserFileMapping> userFileMappings = userFileMappingDao.selectByUserId(CurrentUserCache.getId());
            if (CollUtil.isNotEmpty(userFileMappings)) {
                List<String> fileIds = userFileMappings.stream().map(UserFileMapping::getFileId).toList();
                files = files.stream().filter(i -> fileIds.contains(i.getId()) || i.getCreatedBy().equals(CurrentUserCache.getId())).toList();
            }
        }
        return files;
    }

    @Override
    public void deleteById(String id) {
        File file = baseDao.getById(id);
        Assert.notNull(file, "id不存在");
        FileVerifier.lockVerify(file);
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                deleteByLowerFile(file);
                baseDao.removeById(file);
                userFileMappingDao.deleteByFileId(file.getId());
            }
        });
        operationLogsService.recordingUpload(getPath(file));
    }

    @Override
    public void move(FileCopyMoveRequest request) {
        // 参数校验
        Assert.notBlank(request.getId(), "id不能为空");
        Assert.notBlank(request.getToId(), "目标id不能为空");
        // 移动的目录
        File moveFile = baseDao.getById(request.getId());
        String path = getPath(moveFile);

        Assert.notNull(moveFile, "移动文件夹不存在");
        FileVerifier.lockVerify(moveFile);
        if (StrUtil.equals(moveFile.getParentId(), request.getToId())) {
            return;
        }
        // 目的目录
        File toFile = null;
        if (StrUtil.isNotBlank(request.getToId())) {
            toFile = baseDao.getById(request.getToId());
            Assert.notNull(toFile, "目的文件夹不存在");
            FileVerifier.lockVerify(toFile);
            Assert.isTrue(toFile.getType() == FileType.FOLDER, "目的文件类型不为文件夹");
        }

        // 需要修改的目录
        List<File> updateDirectories = new ArrayList<>();
        // 需要删除的目录
        List<File> delDirectories = new ArrayList<>();

        // 移动处理
        doHandleMove(request.getToId(), updateDirectories, delDirectories, moveFile, baseDao.selectByParentId(request.getToId()));

        // 进行保存
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                baseDao.updateBatchById(updateDirectories);
                baseDao.removeBatchByIds(delDirectories);
            }
        });
        operationLogsService.recordingMove(path, getPath(toFile));
    }

    @Override
    public void downLoadFileByIds(List<String> ids, HttpServletResponse response) throws IOException {
        if (ids == null || ids.isEmpty()) {
            ids = new ArrayList<>();
            ids.add(null);
        }
        List<File> list = baseDao.list();
        if (CollUtil.isEmpty(list)) return;
        Map<String, File> fileMap = new HashMap<>();
        ListMultimap<String, File> parent2FileMap = ArrayListMultimap.create();
        list.forEach(i -> {
            fileMap.put(i.getId(), i);
            parent2FileMap.put(i.getParentId(), i);
        });
        List<FileTreeDTO> files = new ArrayList<>();
        for (String pid : ids) {
            files.addAll(TreeUtil.getDirectoryTreeData(ConvertUtil.convertList(list, FileTreeDTO.class), pid));
        }
        if (!files.isEmpty()) {
            if (files.size() == 1 && files.getFirst().getType() == FileType.FILE) {
                downLoadFile(files.getFirst().getFileUrl(), files.getFirst().getName(), response, 0);
            } else {
                String yyyyMmDdHhMmSs = DateUtil.format(new Date(), "yyyy_MM_dd_HH_mm_ss");
                Random random = new Random();
                String tmpFolderName = yyyyMmDdHhMmSs + "_" + random.nextInt(1000);
                java.io.File tmpFolder = new java.io.File(tmpFolderName);
                if (tmpFolder.exists() || tmpFolder.mkdirs()) {
                    for (FileTreeDTO file : files) {
                        switch (file.getType()) {
                            case FILE -> {
                                List<String> parentFileName = allUpperLevelFolder(file.getParentId(), fileMap);
                                String newFileName = tmpFolderName + "/" + StrUtil.join("-", parentFileName) + "-" + file.getName();
                                java.io.File file11 = new java.io.File(newFileName);
                                if (file11.exists() || file11.createNewFile()) {
                                    FileUtil.copyFile(file.getFileUrl(), newFileName, StandardCopyOption.REPLACE_EXISTING);
                                }
                            }
                            case FOLDER -> {
                                createOriginalFolder(tmpFolderName, file);
                            }
                        }
                    }
                    java.io.File zip = ZipUtil.zip(tmpFolder);
                    downLoadFile(zip, tmpFolderName + ".zip", response, 0);
                    FileUtil.del(tmpFolder);
                    FileUtil.del(zip);
                }
            }
        }
    }


    // 获取所有所有上级文件夹
    private List<String> allUpperLevelFolder(String parentId, Map<String, File> fileMap) {
        if (StrUtil.isBlank(parentId)) return new ArrayList<>();
        File parentFile = fileMap.get(parentId);
        if (parentFile != null && parentFile.getType() == FileType.FOLDER) {
            if (parentFile.getParentId() != null) {
                List<String> uppers = allUpperLevelFolder(parentFile.getParentId(), fileMap);
                uppers.add(parentFile.getName());
                return uppers;
            } else {
                return CollUtil.newArrayList(parentFile.getName());
            }
        }
        return new ArrayList<>();
    }

    @Override
    public String getPath(File file) {
        List<String> list = allUpperLevelFolder(file.getParentId());
        return StrUtil.join("/", list) + "/" + file.getName();
    }

    @Override
    public String getPath(String id) {
        File file = baseDao.getById(id);
        Assert.notNull(file, "文件不存在");
        List<String> list = allUpperLevelFolder(file.getParentId());
        return StrUtil.join("/", list) + "/" + file.getName();
    }

    @Override
    public void replaceFile(String id, String name, MultipartFile multipartFilet) throws IOException {
        File file = baseDao.getById(id);
        Assert.notNull(file, "id不存在");
        FileVerifier.lockVerify(file);

        Assert.isTrue(file.getType() == FileType.FILE, "文件夹不能替换");
        File duplicateFile = baseDao.selectByParentIdAndName(file.getParentId(), name);
        Assert.isTrue(duplicateFile == null || duplicateFile.getId().equals(id), "文件名重复");
        FileUtil.del(file.getFileUrl());
        java.io.File newFile = FileUtils.downLoad(FileUtils.toFileName(name), multipartFilet.getInputStream());
        Assert.notNull(newFile, "文件上传失败");
        file.setName(name);
        file.setFileUrl(newFile.getPath());
        file.updateById();
    }

    @Override
    public void lock(String id) {
        File file = baseDao.getById(id);
        Assert.notNull(file, "id不存在");
        List<File> allChildren = getAllChildren(file);
        allChildren.add(file);
        allChildren.forEach(i -> i.setIsLock(true));
        baseDao.updateBatchById(allChildren);
    }

    public List<File> getAllChildren(File file) {
        List<File> res = new ArrayList<>();
        if (file.getType() == FileType.FOLDER) {
            List<File> files = baseDao.selectByParentId(file.getId());
            if (CollUtil.isNotEmpty(files)) files.forEach(i -> res.addAll(getAllChildren(i)));
            res.addAll(files);
        }
        return res;
    }

    public void createOriginalFolder(String prefix, FileTreeDTO file) throws IOException {
        String path = prefix + "/" + file.getName();
        java.io.File file11 = new java.io.File(path);
        switch (file.getType()) {
            case FILE -> {
                if (file11.exists() || file11.createNewFile()) {
                    try {
                        FileUtil.copyFile(file.getFileUrl(), path, StandardCopyOption.REPLACE_EXISTING);
                    } catch (Exception e) {
                        log.error("{}文件下载失败", file.getName(), e);
                    }

                }
            }
            case FOLDER -> {
                if (file11.exists() || file11.mkdirs()) {
                    if (CollUtil.isNotEmpty(file.getChildren())) {
                        for (FileTreeDTO child : file.getChildren()) {
                            createOriginalFolder(path, child);
                        }
                    }
                }
            }
        }
    }

    @Override
    public void unlock(String id) {
        File file = baseDao.getById(id);
        Assert.notNull(file, "id不存在");
        List<File> files = getAllUpperFolder(file.getParentId());
        files.add(file);
        files.forEach(i -> i.setIsLock(false));
        baseDao.updateBatchById(files);
    }

    @Override
    public void cancelDelTag(String id) {
        File file = baseDao.getById(id);
        Assert.notNull(file, "id不存在");
        file.setDelTag(false);
        baseDao.updateById(file);
    }

    @Override
    public void delTag(String id) {
        File file = baseDao.getById(id);
        Assert.notNull(file, "id不存在");
        file.setDelTag(true);
        baseDao.updateById(file);
    }

    // 获取所有所有上级文件夹
    private List<File> getAllUpperFolder(String parentId) {
        if (StrUtil.isBlank(parentId)) return new ArrayList<>();
        File parentFile = this.baseDao.getById(parentId);
        if (parentFile != null && parentFile.getType() == FileType.FOLDER) {
            if (parentFile.getParentId() != null) {
                List<File> uppers = getAllUpperFolder(parentFile.getParentId());
                uppers.add(parentFile);
                return uppers;
            } else {
                return CollUtil.newArrayList(parentFile);
            }
        }
        return new ArrayList<>();
    }

    public static void main(String[] args) {
//        java.io.File tempFile = FileUtil.createTempFile();
//        System.out.printf(tempFile.getName());
        ListMultimap<String, File> parent2FileMap = ArrayListMultimap.create();
        parent2FileMap.put(null, new File());
        parent2FileMap.put("1", new File());

//        java.io.File file = new java.io.File("A");
//        java.io.File file1 = new java.io.File("B");
//        if (!file.exists()) file.mkdirs();
//        if (!file1.exists()) file1.mkdirs();
//
//        FileUtil.move(file, file1, false);
    }

    private void doHandleMove(String toId, List<File> updateDirectories,
                              List<File> delDirectories, File moveDirectory,
                              List<File> siblingDirectories) {
        // 进行分类
        // 同级文件
        Set<String> fileLabelSet = new HashSet<>();
        // 同级文件夹
        List<File> folderDirectories = new ArrayList<>();
        if (CollUtil.isNotEmpty(siblingDirectories)) {
            for (File siblingDirectory : siblingDirectories) {
                if (siblingDirectory.getType() == FileType.FILE) {
                    fileLabelSet.add(siblingDirectory.getName());
                } else if (siblingDirectory.getType() == FileType.FOLDER) {
                    folderDirectories.add(siblingDirectory);
                }
            }
        }
        for (File addDirectory : updateDirectories) {
            fileLabelSet.add(addDirectory.getName());
        }

        // 移动的为文件
        if (moveDirectory.getType() == FileType.FILE) {
            String newLabel = doHandleRepeatLabel(fileLabelSet, moveDirectory.getName());
            moveDirectory.setName(newLabel);
            moveDirectory.setParentId(toId);

            updateDirectories.add(moveDirectory);

        } else if (moveDirectory.getType() == FileType.FOLDER) {
            // 移动的为文件夹
            Boolean repeat = false;
            if (CollUtil.isNotEmpty(folderDirectories)) {
                for (File folderDirectory : folderDirectories) {
                    if (folderDirectory.getName().equals(moveDirectory.getName())) {
                        repeat = true;
                        // 删除之前的文件夹
                        delDirectories.add(moveDirectory);

                        List<File> children = baseDao.selectByParentId(moveDirectory.getId());
                        if (CollUtil.isNotEmpty(children)) {
                            for (File child : children) {
                                doHandleMove(folderDirectory.getId(), updateDirectories, delDirectories, child, baseDao.selectByParentId(folderDirectory.getId()));
                            }
                        }
                        break;
                    }
                }
            }

            if (!repeat) {
                moveDirectory.setParentId(toId);
                updateDirectories.add(moveDirectory);
            }
        }
    }

    void deleteByLowerFile(File file) {
        if (file.getType() == FileType.FOLDER) {
            List<File> files = baseDao.selectByParentId(file.getId());
            if (CollUtil.isNotEmpty(files)) files.forEach(i -> {
                FileVerifier.lockVerify(i);
                deleteByLowerFile(i);
            });
            baseDao.removeBatchByIds(files);
        }
    }


    private String doHandleRepeatLabel(Set<String> fileLabelSet, String label) {
        if (CollUtil.isNotEmpty(fileLabelSet) && fileLabelSet.contains(label)) {
            return doHandleRepeatLabel(fileLabelSet, label + "-副本");
        }
        return label;
    }

    @Override
    public void downLoadFile(String filePath, String fileName, HttpServletResponse response, Integer signal) {
        java.io.File file = new java.io.File(filePath);
        if (!file.exists()) return;
        downLoadFile(file, fileName, response, signal);
    }

    private static void downLoadFile(java.io.File file, String fileName, HttpServletResponse response, Integer signal) {
        try (ServletOutputStream os = response.getOutputStream();
             InputStream inputStream = new FileInputStream(file)) {
            if (fileName.endsWith("zip")) {
                response.setContentType("application/zip");
            } else if (fileName.endsWith("pdf")) {
                response.setContentType("application/pdf");
            } else {
                StringBuilder contentType = new StringBuilder("application/octet-stream;");
                if (fileName.endsWith(".docx")) {
                    contentType.append("charset=ISO8859-1");
                }
                response.setContentType(contentType.toString());
            }

            response.setCharacterEncoding("UTF-8");
            response.addHeader("Pargam", "no-cache");
            response.addHeader("Cache-Control", "no-cache");
            if (fileName.endsWith(".json.gz")) {
                response.addHeader("Content-Encoding", "gzip");
            }
            String name = fileName;
            int i = fileName.lastIndexOf("/");
            if (i != -1) {
                name = fileName.substring(i + 1);
            }
            String encodedFileName = URLEncoder.encode(name, StandardCharsets.UTF_8);
            if (signal == null || signal == 0) {
                response.setHeader("Content-Disposition", "attachment; filename*=" + encodedFileName);
            } else {
                response.setHeader("Content-Disposition", null);
            }
            IOUtils.copy(inputStream, os);
            os.flush();
        } catch (Exception e) {
            log.error("{} 下载失败", fileName, e);
            response.reset();
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json");
            try (PrintWriter out = response.getWriter()) {
                out.println(JSONUtil.parse(CommonResult.failed()));
                out.flush();
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        }
    }
}
